#include "IOServicePool.hpp"

#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <IIOSServiceFactory.hpp>
#include <IIOService.hpp>
#include <IWork.hpp>

IOServicePool::IOServicePool(IIoServiceFactory& ioServicesFactory, const size_t poolSize)
	: m_ioServicesFactory(ioServicesFactory)
	, m_nextIoService(0)
{
	if (poolSize == 0)
	{
		throw std::runtime_error("poolSize is 0");
	}

	// Give all the io_services work to do so that their run() functions will not
	// exit until they are explicitly stopped.
	for (size_t i = 0; i < poolSize; ++i)
	{
		IoServicePtr ioService(m_ioServicesFactory.createIoService());
		WorkPtr work(ioService->createWorkForThis());
		m_ioServices.push_back(ioService);
		m_works.push_back(work);
	}
}

IOServicePool::~IOServicePool()
{
}

void IOServicePool::run()
{
	typedef std::shared_ptr<boost::thread> ThreadPtr;
	std::vector<ThreadPtr> threads;

	std::for_each(m_ioServices.begin(), m_ioServices.end(), [&threads](IoServicePtr ioService)
	{
		threads.push_back(ThreadPtr(new boost::thread(std::bind(&IIOService::run, ioService))));
	});
	
	std::for_each(threads.begin(), threads.end(), [](ThreadPtr thread){ thread->join(); });
}

void IOServicePool::stop()
{
	std::for_each(m_ioServices.begin(), m_ioServices.end(), [](IoServicePtr ioService){ ioService->stop(); });
}

IIOService& IOServicePool::getIoService()
{
	// Use a round-robin scheme to choose the next io_service to use.
	IIOService& io_service = *m_ioServices[m_nextIoService];
	++m_nextIoService;
	if (m_nextIoService == m_ioServices.size())
	{
		m_nextIoService = 0;
	}
	
	return io_service;
}